package server

import (
	"net/http"
	"os"
	"path/filepath"
	"sieve_admin_server_userpass/lib/config"
	"sieve_admin_server_userpass/lib/json"
	"sieve_admin_server_userpass/lib/log"
	"sieve_admin_server_userpass/lib/net/http/gzip"
	"strconv"
	"strings"
	"time"
)

var (
	fPort         = config.Int("server.port", 9900, "http port")
	fTlsCert      = config.String("tls.cert", "", "tls cert file")
	fTlsKey       = config.String("tls.key", "", "tls key file")
	fWebRoot      = config.String("web.root", filepath.Join(filepath.Dir(os.Args[0]), "../www"), "web root")
	fWebStatic    = config.Strings("web.static", []string{"static"}, "web static dirs")
	fWebVersion   = config.String("web.version", "", "web version")
	fPathRoot     = config.String("web.pathroot", "/", "path root")
	ftitle        = config.String("web.title", "", "web title")
	fDebugMode    = config.Bool("debug.enable", false, "debug模式")
	fDebugSession = config.String("debug.session", "", "debug模式万能session")
)

func Run() error {
	http.HandleFunc(*fPathRoot+"login", handleLogin)
	http.HandleFunc(*fPathRoot+"logout", handleLogout)
	for _, p := range *fWebStatic {
		if p != "" {
			http.HandleFunc(*fPathRoot+p+"/", func(w http.ResponseWriter, r *http.Request) {
				http.ServeFile(w, r, filepath.Join(*fWebRoot, r.URL.Path[1:]))
			})
		}
	}
	http.HandleFunc("/", handleHttp)
	sv := &http.Server{Addr: ":" + strconv.Itoa(*fPort)}
	sv.SetKeepAlivesEnabled(false)
	if *fTlsCert != "" && *fTlsKey != "" {
		return sv.ListenAndServeTLS(*fTlsCert, *fTlsKey)
	} else {
		return sv.ListenAndServe()
	}
}

func DebugMode() bool { return *fDebugMode }

func handleHttp(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Access-Control-Allow-Origin", "*")
	now := time.Now()

	if !strings.HasPrefix(r.URL.Path, *fPathRoot) {
		http.NotFound(w, r)
		return
	}
	ss := strings.Split(r.URL.Path[len(*fPathRoot):], "/")
	mod := ss[0]
	api := ""
	if len(ss) > 1 {
		mod = strings.Join(ss[:len(ss)-1], "/")
		api = ss[len(ss)-1]
	}

	if mod == "" {
		if GetLoginUid(r) == 0 {
			http.Redirect(w, r, *fPathRoot+"login", http.StatusFound)
		} else {
			handleIndex(w, r)
		}
		return
	}

	if !*fDebugMode && r.Method != http.MethodPost {
		w.WriteHeader(http.StatusForbidden)
		w.Write([]byte("POST Only"))
		return
	}

	if err := r.ParseForm(); err != nil {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(err.Error()))
		return
	}

	if *fWebVersion != "" && r.PostForm.Get("currVer") != *fWebVersion {
		w.WriteHeader(http.StatusForbidden)
		w.Write([]byte("有新版更新，请刷新浏览器后尝试！"))
		return
	}

	uid := GetLoginUid(r)
	if uid == 0 {
		w.WriteHeader(http.StatusForbidden)
		w.Write([]byte("not login"))
		return
	}
	m, ok := mods[mod]
	if !ok {
		http.NotFound(w, r)
		return
	}
	fn, ok := m.apis[api]
	if !ok {
		http.NotFound(w, r)
		return
	}

	perm, err := parseApiPerm(api)
	if err != nil {
		w.WriteHeader(http.StatusForbidden)
		w.Write([]byte("invalid api format"))
		return
	}
	perm = mod + "." + perm

	if !checkUserPerm(uid, perm) {
		w.WriteHeader(http.StatusForbidden)
		w.Write([]byte("permission denied"))
		return
	}
	defer addOpLog(uid, r, now)
	ret := fn(&Context{
		Uid:  uid,
		Args: r.PostForm,
		Http: &ContextHttp{w, r},
	})
	if ret == nil {
		return
	}
	if err, ok := ret.(error); ok {
		log.Err(r.URL.Path, err)
		w.WriteHeader(http.StatusForbidden)
		w.Write([]byte(err.Error()))
		return
	}
	var b []byte
	switch v := ret.(type) {
	case string:
		b = []byte(v)
	default:
		b, _ = json.Marshal(v)
	}
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	gzip.Write(w, r, b)
}

func handleIndex(w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, filepath.Join(*fWebRoot, "index.html"))
}

func GetWebRootPath() string {
	return *fWebRoot
}
